#ifndef STRONGPTR_H__MINCOM__COMMONKIT__INCLUDED_
#define STRONGPTR_H__MINCOM__COMMONKIT__INCLUDED_

namespace MinCOM
{

	template
	<
		typename T,
		bool Strong = true,
		class OwnershipPolicy = TwoRefCounts< T >,
		class ConversionPolicy = Loki::DisallowConversion,
		template < class > class CheckingPolicy = Loki::AssertCheck,
		template < class > class ResetPolicy = Loki::CantResetWithStrong,
		template < class > class DeletePolicy = Loki::DeleteNothing,
		template < class > class ConstnessPolicy = Loki::LOKI_DEFAULT_CONSTNESS
	>
	class StrongPtr
		: public OwnershipPolicy
		, public ConversionPolicy
		, public CheckingPolicy< T * >
		, public ResetPolicy< T >
		, public DeletePolicy< T >
	{
		typedef ConversionPolicy CP;
		typedef CheckingPolicy< T * > KP;
		typedef ResetPolicy< T > RP;
		typedef DeletePolicy< T > DP;

	public:

		typedef OwnershipPolicy OP;

		typedef T Type_;    

		typedef T * StoredType;    // the type of the pointer
		typedef T * PointerType;   // type returned by operator->
		typedef T & ReferenceType; // type returned by operator*

		typedef typename ConstnessPolicy< T >::Type * ConstPointerType;
		typedef typename ConstnessPolicy< T >::Type & ConstReferenceType;

	private:
		struct NeverMatched;

	#ifdef LOKI_SMARTPTR_CONVERSION_CONSTRUCTOR_POLICY
		typedef typename Select< CP::allow, const StoredType&, NeverMatched>::Result ImplicitArg;
		typedef typename Select<!CP::allow, const StoredType&, NeverMatched>::Result ExplicitArg;
	#else
		typedef const StoredType& ImplicitArg;
		typedef typename Loki::Select<false, const StoredType&, NeverMatched>::Result ExplicitArg;
	#endif

	public:

		StrongPtr( void ) : OP( Strong )
		{
			KP::OnDefault( GetPointer() );
		}

		explicit StrongPtr( ExplicitArg p ) : OP( p, Strong )
		{
			KP::OnInit( GetPointer() );
		}

		StrongPtr( ImplicitArg p ) : OP( p, Strong )
		{
			KP::OnInit( GetPointer() );
		}

		StrongPtr( const StrongPtr & rhs )
			: OP( rhs, Strong ), CP( rhs ), KP( rhs ), DP( rhs )
		{
		}

		template
		<
			typename T1,
			bool S1,
			class OP1,
			class CP1,
			template < class > class KP1,
			template < class > class RP1,
			template < class > class DP1,
			template < class > class CNP1
		>
		StrongPtr(
			const StrongPtr< T1, S1, OP1, CP1, KP1, RP1, DP1, CNP1 > & rhs )
			: OP( rhs, Strong )
		{
		}

		template
		<
			typename T1,
			bool S1,
			class OP1,
			class CP1,
			template < class > class KP1,
			template < class > class RP1,
			template < class > class DP1,
			template < class > class CNP1
		>
		StrongPtr(
			StrongPtr< T1, S1, OP1, CP1, KP1, RP1, DP1, CNP1 > & rhs )
			: OP( rhs, Strong )
		{
		}

		StrongPtr( Loki::RefToValue< StrongPtr > rhs )
			: OP( rhs, Strong ), KP( rhs ), CP( rhs ), DP( rhs )
		{
		}

		operator Loki::RefToValue< StrongPtr >( void )
		{
			return Loki::RefToValue< StrongPtr >( *this );
		}

		StrongPtr & operator = ( const StrongPtr & rhs )
		{
			if ( GetPointer() != rhs.GetPointer() )
			{
				StrongPtr temp( rhs );
				temp.Swap( *this );
			}
			return *this;
		}

		StrongPtr & operator = ( T * p )
		{
			if ( GetPointer() != p )
			{
				StrongPtr temp( p );
				Swap( temp );
			}
			return *this;
		}

		template
		<
			typename T1,
			bool S1,
			class OP1,
			class CP1,
			template < class > class KP1,
			template < class > class RP1,
			template < class > class DP1,
			template < class > class CNP1
		>
		StrongPtr & operator = (
			const StrongPtr< T1, S1, OP1, CP1, KP1, RP1, DP1, CNP1 > & rhs )
		{
			if ( GetPointer() != rhs.GetPointer() )
			{
				StrongPtr temp( rhs );
				temp.Swap( *this );
			}
			return *this;
		}

		bool IsStrong( void ) const
		{
			return Strong;
		}

		void Swap( StrongPtr & rhs )
		{
			OP::Swap( rhs );
			CP::Swap( rhs );
			KP::Swap( rhs );
			DP::Swap( rhs );
		}

		~StrongPtr()
		{
			if ( OP::Release( Strong ) )
			{
				// Must zap the pointer before deleteing the object. Otherwise a
				// cycle of weak pointers will lead to recursion, which leads to
				// to deleting the shared object multiple times, which leads to
				// undefined behavior.  Therefore, this must get pointer before
				// zapping it, and then delete the temp pointer.
				
				// MC. There is no more need to check whether pointer is set to NULL.
				// Must zap it on any case
				/*T * p = GetPointer();
				if ( p != 0 )
				{*/
					OP::ZapPointer();
				/*	DP::Delete( p );
				} */
			}
		}

		friend bool ReleaseAll( StrongPtr & sp,
			typename StrongPtr::StoredType & p )
		{
			if ( !sp.RP::OnReleaseAll( sp.IsStrong() || sp.OP::HasStrongPointer() ) )
			{
				return false;
			}
			p = sp.GetPointer();
			sp.OP::SetPointer( sp.DP::Default() );
			return true;
		}

		friend bool ResetAll( StrongPtr & sp,
			typename StrongPtr::StoredType p )
		{
			if ( sp.OP::GetPointer() == p )
			{
				return true;
			}
			if ( !sp.RP::OnResetAll( sp.IsStrong() || sp.OP::HasStrongPointer() ) )
			{
				return false;
			}
			sp.DP::Delete( sp.GetPointer() );
			sp.OP::SetPointer( p );
			return true;
		}

		/** Locks StrongPtr so other threads can't affect pointer.  Requires the
		 OwnershipPolicy to have Lock function.
		 */
		void Lock( void )
		{
			OP::Lock();
		}

		/** Unlocks StrongPtr so other threads can affect pointer.  Requires the
		 OwnershipPolicy to have Unlock function.
		 */
		void Unlock( void )
		{
			OP::Unlock();
		}

		PointerType operator -> ()
		{
			KP::OnDereference( GetPointer() );
			return GetPointer();
		}

		ConstPointerType operator -> () const
		{
			KP::OnDereference( GetPointer() );
			return GetPointer();
		}

		ReferenceType operator * ()
		{
			KP::OnDereference( GetPointer() );
			return * GetPointer();
		}

		ConstReferenceType operator * () const
		{
			KP::OnDereference( GetPointer() );
			return * GetPointer();
		}

		/// Equality comparison operator is templated to handle ambiguity.
		template
		<
			typename T1,
			bool S1,
			class OP1,
			class CP1,
			template < class > class KP1,
			template < class > class RP1,
			template < class > class DP1,
			template < class > class CNP1
		>
		bool operator == (
			const StrongPtr< T1, S1, OP1, CP1, KP1, RP1, DP1, CNP1 > & rhs ) const
		{
			return ( GetPointer() == rhs.GetPointer() );
		}

		/// Inequality comparison operator is templated to handle ambiguity.
		template
		<
			typename T1,
			bool S1,
			class OP1,
			class CP1,
			template < class > class KP1,
			template < class > class RP1,
			template < class > class DP1,
			template < class > class CNP1
		>
		bool operator != (
			const StrongPtr< T1, S1, OP1, CP1, KP1, RP1, DP1, CNP1 > & rhs ) const
		{
			return ( GetPointer() != rhs.GetPointer() );
		}

		/// Less-than comparison operator is templated to handle ambiguity.
		template
		<
			typename T1,
			bool S1,
			class OP1,
			class CP1,
			template < class > class KP1,
			template < class > class RP1,
			template < class > class DP1,
			template < class > class CNP1
		>
		bool operator < (
			const StrongPtr< T1, S1, OP1, CP1, KP1, RP1, DP1, CNP1 > & rhs ) const
		{
			return ( GetPointer() < rhs.GetPointer() );
		}

		/// Greater-than comparison operator is templated to handle ambiguity.
		template
		<
			typename T1,
			bool S1,
			class OP1,
			class CP1,
			template < class > class KP1,
			template < class > class RP1,
			template < class > class DP1,
			template < class > class CNP1
		>
		inline bool operator > (
			const StrongPtr< T1, S1, OP1, CP1, KP1, RP1, DP1, CNP1 > & rhs ) const
		{
			return ( rhs.GetPointer() < GetPointer() );
		}

		/// Less-than-or-equal-to operator is templated to handle ambiguity.
		template
		<
			typename T1,
			bool S1,
			class OP1,
			class CP1,
			template < class > class KP1,
			template < class > class RP1,
			template < class > class DP1,
			template < class > class CNP1
		>
		inline bool operator <= (
			const StrongPtr< T1, S1, OP1, CP1, KP1, RP1, DP1, CNP1 > & rhs ) const
		{
			return !( rhs.GetPointer() < GetPointer() );
		}

		/// Greater-than-or-equal-to operator is templated to handle ambiguity.
		template
		<
			typename T1,
			bool S1,
			class OP1,
			class CP1,
			template < class > class KP1,
			template < class > class RP1,
			template < class > class DP1,
			template < class > class CNP1
		>
		inline bool operator >= (
			const StrongPtr< T1, S1, OP1, CP1, KP1, RP1, DP1, CNP1 > & rhs ) const
		{
			return !( GetPointer() < rhs.GetPointer() );
		}

		inline bool operator ! () const // Enables "if ( !sp ) ..."
		{
			return ( 0 == OP::GetPointer() );
		}

		inline PointerType GetPointer( void )
		{
			return reinterpret_cast< PointerType >( OP::GetPointer() );
		}

		inline ConstPointerType GetPointer( void ) const
		{
			return reinterpret_cast< ConstPointerType >( OP::GetPointer() );
		}

	protected:

		/** Merges ownership of two StrongPtr's that point to same shared object
		  but are not copointers.  Requires Merge function in OwnershipPolicy.
		  \return True for success, false if not pointer to same object.
		 */
		template
		<
			typename T1,
			bool S1,
			class OP1,
			class CP1,
			template < class > class KP1,
			template < class > class RP1,
			template < class > class DP1,
			template < class > class CNP1
		>
		bool Merge( StrongPtr< T1, S1, OP1, CP1, KP1, RP1, DP1, CNP1 > & rhs )
		{
			return OP::Merge( *reinterpret_cast< StrongPtr* >(&rhs) );
		}

	private:

		inline ReferenceType GetPointerRef( void )
		{
			return reinterpret_cast< ReferenceType >( OP::GetPointerRef() );
		}

		inline ConstReferenceType GetPointerRef( void ) const
		{
			return reinterpret_cast< ConstReferenceType >( OP::GetPointerRef() );
		}

		// Helper for enabling 'if (sp)'
		struct Tester
		{
			Tester(int) {}
			void dummy() {}
		};
	    
		typedef void (Tester::*unspecified_boolean_type_)();

		typedef typename Loki::Select< CP::allow, Tester, unspecified_boolean_type_ >::Result
			unspecified_boolean_type;

	public:
		// enable 'if (sp)'
		operator unspecified_boolean_type() const
		{
			return !*this ? 0 : &Tester::dummy;
		}

	private:
		// Helper for disallowing automatic conversion
		struct Insipid
		{
			Insipid(PointerType) {}
		};
	    
		typedef typename Loki::Select< CP::allow, PointerType, Insipid >::Result
			AutomaticConversionResult;

	public:        
		operator AutomaticConversionResult() const
		{
			return GetPointer();
		}

	};

	// ----------------------------------------------------------------------------

	// free comparison operators for class template StrongPtr

	///  operator== for lhs = StrongPtr, rhs = raw pointer
	///  \ingroup SmartPointerGroup
	template
	<
		typename U,
		typename T,
		bool S,
		class OP,
		class CP,
		template < class > class KP,
		template < class > class RP,
		template < class > class DP,
		template < class > class CNP
	>
	inline bool operator == (
		const StrongPtr< T, S, OP, CP, KP, RP, DP, CNP > & lhs, U * rhs )
	{
		return ( lhs.GetPointer() == rhs );
	}

	///  operator== for lhs = raw pointer, rhs = StrongPtr
	///  \ingroup SmartPointerGroup
	template
	<
		typename U,
		typename T,
		bool S,
		class OP,
		class CP,
		template < class > class KP,
		template < class > class RP,
		template < class > class DP,
		template < class > class CNP
	>
	inline bool operator == ( U * lhs,
		const StrongPtr< T, S, OP, CP, KP, RP, DP, CNP > & rhs )
	{
		return ( rhs.GetPointer() == lhs );
	}

	///  operator!= for lhs = StrongPtr, rhs = raw pointer
	///  \ingroup SmartPointerGroup
	template
	<
		typename U,
		typename T,
		bool S,
		class OP,
		class CP,
		template < class > class KP,
		template < class > class RP,
		template < class > class DP,
		template < class > class CNP
	>
	inline bool operator != (
		const StrongPtr< T, S, OP, CP, KP, RP, DP, CNP > & lhs, U * rhs )
	{
		return !( lhs.GetPointer() == rhs );
	}

	///  operator!= for lhs = raw pointer, rhs = StrongPtr
	///  \ingroup SmartPointerGroup
	template
	<
		typename U,
		typename T,
		bool S,
		class OP,
		class CP,
		template < class > class KP,
		template < class > class RP,
		template < class > class DP,
		template < class > class CNP
	>
	inline bool operator != ( U * lhs,
		const StrongPtr< T, S, OP, CP, KP, RP, DP, CNP > & rhs )
	{
		return ( rhs.GetPointer() != lhs );
	}

	///  operator< for lhs = StrongPtr, rhs = raw pointer
	///  \ingroup SmartPointerGroup
	template
	<
		typename U,
		typename T,
		bool S,
		class OP,
		class CP,
		template < class > class KP,
		template < class > class RP,
		template < class > class DP,
		template < class > class CNP
	>
	inline bool operator < (
		const StrongPtr< T, S, OP, CP, KP, RP, DP, CNP > & lhs, U * rhs )
	{
		return ( lhs.GetPointer() < rhs );
	}

	///  operator< for lhs = raw pointer, rhs = StrongPtr
	///  \ingroup SmartPointerGroup
	template
	<
		typename U,
		typename T,
		bool S,
		class OP,
		class CP,
		template < class > class KP,
		template < class > class RP,
		template < class > class DP,
		template < class > class CNP
	>
	inline bool operator < ( U * lhs,
		const StrongPtr< T, S, OP, CP, KP, RP, DP, CNP > & rhs )
	{
		return ( lhs < rhs.GetPointer() );
	}

	//  operator> for lhs = StrongPtr, rhs = raw pointer
	///  \ingroup SmartPointerGroup
	template
	<
		typename U,
		typename T,
		bool S,
		class OP,
		class CP,
		template < class > class KP,
		template < class > class RP,
		template < class > class DP,
		template < class > class CNP
	>
	inline bool operator > (
		const StrongPtr< T, S, OP, CP, KP, RP, DP, CNP > & lhs, U * rhs )
	{
		return ( rhs < lhs.GetPointer() );
	}

	///  operator> for lhs = raw pointer, rhs = StrongPtr
	///  \ingroup SmartPointerGroup
	template
	<
		typename U,
		typename T,
		bool S,
		class OP,
		class CP,
		template < class > class KP,
		template < class > class RP,
		template < class > class DP,
		template < class > class CNP
	>
	inline bool operator > ( U * lhs,
		const StrongPtr< T, S, OP, CP, KP, RP, DP, CNP > & rhs )
	{
		return ( rhs.GetPointer() < lhs );
	}

	///  operator<= for lhs = StrongPtr, rhs = raw pointer
	///  \ingroup SmartPointerGroup
	template
	<
		typename U,
		typename T,
		bool S,
		class OP,
		class CP,
		template < class > class KP,
		template < class > class RP,
		template < class > class DP,
		template < class > class CNP
	>
	inline bool operator <= (
		const StrongPtr< T, S, OP, CP, KP, RP, DP, CNP > & lhs, U * rhs )
	{
		return !( rhs < lhs.GetPointer() );
	}

	///  operator<= for lhs = raw pointer, rhs = StrongPtr
	///  \ingroup SmartPointerGroup
	template
	<
		typename U,
		typename T,
		bool S,
		class OP,
		class CP,
		template < class > class KP,
		template < class > class RP,
		template < class > class DP,
		template < class > class CNP
	>
	inline bool operator <= ( U * lhs,
		const StrongPtr< T, S, OP, CP, KP, RP, DP, CNP > & rhs )
	{
		return !( rhs.GetPointer() < lhs );
	}

	///  operator>= for lhs = StrongPtr, rhs = raw pointer
	///  \ingroup SmartPointerGroup
	template
	<
		typename U,
		typename T,
		bool S,
		class OP,
		class CP,
		template < class > class KP,
		template < class > class RP,
		template < class > class DP,
		template < class > class CNP
	>
	inline bool operator >= (
		const StrongPtr< T, S, OP, CP, KP, RP, DP, CNP > & lhs, U * rhs )
	{
		return !( lhs.GetPointer() < rhs );
	}

	///  operator>= for lhs = raw pointer, rhs = StrongPtr
	///  \ingroup SmartPointerGroup
	template
	<
		typename U,
		typename T,
		bool S,
		class OP,
		class CP,
		template < class > class KP,
		template < class > class RP,
		template < class > class DP,
		template < class > class CNP
	>
	inline bool operator >= ( U * lhs,
		const StrongPtr< T, S, OP, CP, KP, RP, DP, CNP > & rhs )
	{
		return !( lhs < rhs.GetPointer() );
	}

}

#endif // !STRONGPTR_H__MINCOM__COMMONKIT__INCLUDED_
